
#define _WIN32_WINNT 0x0500
#include <process.h>

#include "Win32Threads.h"
#include "Common/Errors.h"
#include "Runtime.Low/Services.h"

namespace ttg
{
	namespace threads
	{
		namespace win32
		{
			//Entry point for new threads.
			unsigned __stdcall entryPoint(void *vdata)
			{
				Win32Thread *t = static_cast<Win32Thread *>(vdata);
				while (true)
					t->work();
			}
		}
	}
}

using namespace ttg;
using namespace ttg::threads::win32;

//-------------------
//--- Win32Thread ---
//-------------------

Win32Thread::Win32Thread(bool shouldstart)
{
	this->winthread = 0;
	this->state = Working;
	isstarted = false;
	shouldterminate = false;
	should_resleep = false;
	if (shouldstart)
		this->start();
}

void Win32Thread::sleep(bool is_fast, bool is_waiting, double wait_time)
{
	sleep_cs.enter();
	if (this->state == Working)
	{
		//Sleaping.
		this->state = is_fast ? SleepingFast : SleepingDeep;
		sleep_cs.leave();
		switch ((is_fast ? 1 : 2) + (is_waiting ? 4 : 8))
		{
			case 5:
				sleep_fast.wait(wait_time); break;
			case 6:
				sleep_slow.wait(wait_time); break;
			case 9:
				sleep_fast.wait(); break;
			case 10:
				sleep_slow.wait(); break;
			default:
				throw ttg::Error(ttg::Error::FATAL, "Win32Thread", "sleep", "Internal error");
		}
		sleep_cs.enter();
		state = Working;
		//Should be terminated?
		if (shouldterminate)
		{
			sleep_cs.leave();
			_endthread();
		}
		sleep_cs.leave();
	}
	else
		sleep_cs.leave();
	//Resleeping.
	if (should_resleep)
	{
		should_resleep = false;
		if (is_fast)
			sleepDeep();
		else
			sleepFast();
	}
}

void Win32Thread::sleepDeep()
{
	sleep(false, false);
}

void Win32Thread::sleepDeep(double seconds)
{
	sleep(false, true, seconds);
}

void Win32Thread::sleepFast()
{
	sleep(true, false);
}

void Win32Thread::sleepFast(double seconds)
{
	sleep(true, true, seconds);
}

void Win32Thread::reSleep(bool fast_sleep)
{
	sleep_cs.enter();
	if (state != 0 && (state == (1 ^ fast_sleep)))
	{
		this->should_resleep = true;
		sleep_cs.leave();
		this->wake();
	}
	else
		sleep_cs.leave();
}

void Win32Thread::wake()
{
	sleep_cs.enter();
	if (this->state != Working)
	{
		if (state == SleepingFast)
			sleep_fast.rise();
		else
			sleep_slow.rise();
	}
	sleep_cs.leave();
}

void Win32Thread::start()
{
	if (!isstarted)
	{
		if ((winthread = _beginthreadex(NULL, 0, entryPoint, this, 0, NULL)) == NULL)
			throw Error(Error::FATAL, "THread", "start", "Cannot start new thread");
	}
}

void Win32Thread::terminate()
{
	if (winthread != 0)
	{
		this->shouldterminate = true;
		if (isSleeping())
			wake();
		while (WaitForSingleObject((HANDLE)winthread, 20) == WAIT_TIMEOUT)
			if (isSleeping())
				wake();
		winthread = 0;
	}
}

bool Win32Thread::isSleeping()
{
	bool res;
	sleep_cs.enter();
	res = this->state != 0;
	sleep_cs.leave();
	return res;
}

void Win32Thread::setIdealCore(size_t core)
{
	if (SetThreadIdealProcessor(GetCurrentThread(), (DWORD)core) < 0)
		ttg::services::addLocalError("Performer", "Performer", "Cannot set ideal processor. WTF?");
}

Win32Thread::~Win32Thread()
{
	terminate();
}
